package com.monkey.luffer.springboot.demo.basis.kcc.s3cmd;

import com.amazonaws.services.s3.AmazonS3;
import com.monkey.luffer.springboot.demo.basis.kcc.utils.ShellUtil;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @author jiahong.liu
 */
public class CompareApollo implements S3Oper {
    public static final String[] SERVICE = {"acct-fileserver","alarm-platform","alert-center",
            "app-manager","business-monitor","ca-sign",
            "core-banking-gateway","encryption-platform","file-transfer",
            "newom", "param-sync", "card-hsm"};
    public static final String[] SERVICE_WITH_EVN = {"portal-gateway", "portal-basic-info","portal-approval-center",
            "portal-audit-service","corebank-common-config","banking-common","bank-gateway-service"};
    public static final String[] SERVICE_WITH_MULTIPLE_NAMESPACE = {"business-center"};
    public static final String[] NAMESPACE = {"transify", "default"};
    public static final String [] ENVS = {"dev1","dev2","sit1","sit2","uat1","uat2","uat3","staging","live","canary", "stagingdr","dr","stagingcanary"};

    public static final String MAIN_DIR = "/deposit_accounting_pay_apollo";
    public static final String DEFAULT_DIR = "/default/apollo.properties";
    public static final String FILE_NAME = "/apollo.properties";

    public static final String ADD = "# +apollo:ns={},public=false,clearNs=false,verb=add";
    public static final String DELETE = "# +apollo:ns={},public=false,clearNs=false,verb=delete";
    public static final String UPDATE = "# +apollo:ns={},public=false,clearNs=false,verb=update";


    Map<String, Map<String, String>> preMap = new HashMap<>();
    Map<String, Map<String, Map<String, String>>> preMapWithEnv = new HashMap<>();
    Map<String, Map<String, Map<String, String>>> preMapWithNamespace = new HashMap<>();

    Map<String, Map<String, String>> curMap = new HashMap<>();
    Map<String, Map<String, Map<String, String>>> curMapWithEnv = new HashMap<>();
    Map<String, Map<String, Map<String, String>>> curMapWithNamespace = new HashMap<>();

    public static final String CHECK_OUT = "git checkout {}";

    public static final String CD_DIR = "cd {}";

    public static final String GIT_PULL = "git pull";

    public static void readFileByLine(File file, Map<String, String> map) throws IOException {
        FileInputStream inputStream = new FileInputStream(file);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String str;
        while((str = bufferedReader.readLine()) != null)
        {
            if(str.trim().startsWith("#") || StringUtils.isBlank(str)) {
                continue;
            }
            int count = hasCount(str, "=");
            String [] temp = str.split("=");
            if (count == 1 && temp.length == 2) {
                map.put(temp[0].trim(), temp[1].trim());
            } else if (temp.length < 2) {
                map.put(temp[0].trim(), "");
            } else {
                int index = str.indexOf("=");
                map.put(str.substring(0,index).trim(),  str.substring(index +1));
            }
        }
    }

    public static void createDir (File file) {
        File parentFile = file.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
            createDir(parentFile);
        }
    }

    public static void writeFile(File file, String content) {
        try {
            createDir(file);
            if(!file.exists()){
                boolean flag = file.createNewFile();
                if (!flag) {
                    System.out.println("Create file failed, please check");
                    return;
                }
            }
            FileWriter fileWriter = new FileWriter(file);
            BufferedWriter bufferWriter = new BufferedWriter(fileWriter);
            bufferWriter.write(content);
            bufferWriter.close();
            System.out.println("Write file done");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    @SneakyThrows
    public void request(AmazonS3 s3, MyCmd cmd) {
        String apolloProjectPath = cmd.getSrcPath();
        String preVersion = cmd.getPreVersion();
        String curVersion = cmd.getCurVersion();
        if (StringUtils.isBlank(preVersion) || StringUtils.isBlank(curVersion)) {
            System.out.println("分支名必输");
            throw new Exception("必输字段");
        }
        String generateFilePath = cmd.getDstPath();
        String cdCommand = CD_DIR.replace("{}", apolloProjectPath);
        String checkoutCommand = CHECK_OUT.replace("{}", preVersion);
        ShellUtil.runShell(cdCommand + "&&" + checkoutCommand + "&&" + GIT_PULL);
        readConfigWithNoEnv(apolloProjectPath + MAIN_DIR, preMap);
        readConfigWithEnv(apolloProjectPath + MAIN_DIR, preMapWithEnv);
        readConfigWithNamespace(apolloProjectPath + MAIN_DIR, preMapWithNamespace);
        checkoutCommand = CHECK_OUT.replace("{}", curVersion);
        ShellUtil.runShell(cdCommand + "&&" + checkoutCommand+ "&&" + GIT_PULL);
        readConfigWithNoEnv(apolloProjectPath + MAIN_DIR, curMap);
        readConfigWithEnv(apolloProjectPath + MAIN_DIR, curMapWithEnv);
        readConfigWithNamespace(apolloProjectPath + MAIN_DIR, curMapWithNamespace);
        compareConfigAndWriteFile(generateFilePath);
        compareConfigWithEnvAndWriteFile(generateFilePath);
        compareConfigWithNamespaceAndWriteFile(generateFilePath);
    }

    @SneakyThrows
    public void readConfigWithNoEnv(String path, Map<String, Map<String, String>> map) {
        for (String service: SERVICE) {
            File file  = new File(path + "/" + service + DEFAULT_DIR);
            if (!file.exists()) {
                map.put(service, new HashMap<>(8));
                continue;
            }
            Map<String, String> config = new HashMap<>(8);
            readFileByLine(file, config);
            map.put(service, config);
        }
    }

    @SneakyThrows
    public void readConfigWithEnv(String path, Map<String, Map<String, Map<String, String>>> map) {
        for (String service: SERVICE_WITH_EVN) {
            Map<String, Map<String, String>> envMap = new HashMap<>(8);
            for (String env: ENVS) {
                Map<String, String> config = new HashMap<>(8);
                File file  = new File(path + "/" + service + "/" + env + DEFAULT_DIR);
                if (!file.exists()) {
                    envMap.put(env, new HashMap<>(8));
                    map.put(service, envMap);
                    continue;
                }
                readFileByLine(file, config);
                envMap.put(env, config);
                map.put(service, envMap);
            }
        }
    }

    @SneakyThrows
    public void readConfigWithNamespace(String path, Map<String, Map<String, Map<String, String>>> map) {
        for (String service: SERVICE_WITH_MULTIPLE_NAMESPACE) {
            Map<String, Map<String, String>> envMap = new HashMap<>(8);
            for (String namespace: NAMESPACE) {
                Map<String, String> config = new HashMap<>(8);
                File file  = new File(path + "/" + service + "/" + namespace + FILE_NAME);
                if (!file.exists()) {
                    envMap.put(namespace, new HashMap<>(8));
                    map.put(service, envMap);
                    continue;
                }
                readFileByLine(file, config);
                envMap.put(namespace, config);
                map.put(service, envMap);
            }
        }
    }

    public void compareConfigAndWriteFile(String destPath) {
        for (String service: SERVICE) {
            Map<String, String> curTempMap = curMap.get(service);
            Map<String, String> preTempMap = preMap.get(service);
            boolean addFlag = false;
            boolean updateFlag = false;
            boolean deleteFlag = false;
            StringBuilder addContent = new StringBuilder();
            StringBuilder deleteContent =  new StringBuilder();
            StringBuilder updateContent =  new StringBuilder();
            for (Map.Entry<String, String> entry: curTempMap.entrySet()) {
                String key = entry.getKey().trim();
                String value = entry.getValue().trim();
                String preValue = preTempMap.get(key);
                if (preValue == null) {
                    addFlag = true;
                    addContent.append(key).append(" = ").append(value).append("\n");
                } else if (!preValue.trim().equals(value)) {
                    updateFlag = true;
                    updateContent.append(key).append(" = ").append(value).append("\n");
                    preTempMap.remove(key);
                } else {
                    preTempMap.remove(key);
                }
            }

            for (Map.Entry<String, String> entry: preTempMap.entrySet()) {
                deleteFlag = true;
                deleteContent.append(entry.getKey().trim()).append(" = ").append(entry.getValue().trim()).append("\n");
            }

            if (addFlag || updateFlag || deleteFlag) {
                String namespace = "application";
                if (addFlag) {
                    addContent.insert(0,ADD.replace("{}", namespace) + "\n");
                }

                if (deleteFlag) {
                    deleteContent.insert(0, DELETE.replace("{}", namespace) + "\n");
                }

                if (updateFlag) {
                    updateContent.insert(0, UPDATE.replace("{}", namespace) + "\n");
                }

                for (String env: ENVS) {
                    String path = destPath + "/" + service + "/" + env + DEFAULT_DIR;
                    writeFile(new File(path), addContent + deleteContent.toString() + updateContent);
                }
            }
        }
    }

    public void compareConfigWithEnvAndWriteFile(String destPath) {
        for (String service: SERVICE_WITH_EVN) {
            Map<String, Map<String, String>> curTempMap = curMapWithEnv.get(service);
            Map<String, Map<String, String>> preTempMap = preMapWithEnv.get(service);
            for (String env: ENVS) {
                boolean addFlag = false;
                boolean updateFlag = false;
                boolean deleteFlag = false;
                Map<String, String> curTemp = curTempMap.get(env);
                Map<String, String> preTemp = preTempMap.get(env);
                StringBuilder addContent = new StringBuilder();
                StringBuilder deleteContent =  new StringBuilder();
                StringBuilder updateContent =  new StringBuilder();
                for (Map.Entry<String, String> entry: curTemp.entrySet()) {
                    String key = entry.getKey().trim();
                    String value = entry.getValue().trim();
                    String preValue = preTemp.get(key);
                    if (preValue == null) {
                        addContent.append(key).append(" = ").append(value).append("\n");
                        addFlag = true;
                    } else if (!preValue.trim().equals(value)) {
                        updateFlag = true;
                        updateContent.append(key).append(" = ").append(value).append("\n");
                        preTemp.remove(key);
                    } else {
                        preTemp.remove(key);
                    }
                }

                for (Map.Entry<String, String> entry: preTemp.entrySet()) {
                    deleteFlag = true;
                    deleteContent.append(entry.getKey().trim()).append(" = ").append(entry.getValue().trim()).append("\n");
                }

                if (addFlag || updateFlag || deleteFlag) {
                    String namespace = "application";
                    if ("banking-common".equals(service)) {
                        namespace = "banking-common-config";
                    }

                    if ("corebank-common-config".equals(service)) {
                        namespace = "corebank-common-config";
                    }

                    if (addFlag) {
                        addContent.insert(0,ADD.replace("{}", namespace) + "\n");
                    }

                    if (deleteFlag) {
                        deleteContent.insert(0, DELETE.replace("{}", namespace) + "\n");
                    }

                    if (updateFlag) {
                        updateContent.insert(0, UPDATE.replace("{}", namespace) + "\n");
                    }

                    String path = destPath + "/" + service + "/" + env + DEFAULT_DIR;
                    writeFile(new File(path), addContent + deleteContent.toString() + updateContent );
                }
            }
        }
    }

    public void compareConfigWithNamespaceAndWriteFile(String destPath) {
        for (String service: SERVICE_WITH_MULTIPLE_NAMESPACE) {
            Map<String, Map<String, String>> curTempMap = curMapWithNamespace.get(service);
            Map<String, Map<String, String>> preTempMap = preMapWithNamespace.get(service);
            boolean addFlag = false;
            boolean updateFlag = false;
            boolean deleteFlag = false;
            StringBuilder addContent = new StringBuilder();
            StringBuilder deleteContent =  new StringBuilder();
            StringBuilder updateContent =  new StringBuilder();
            for (String namespace: NAMESPACE) {
                Map<String, String> curTemp = curTempMap.get(namespace);
                Map<String, String> preTemp = preTempMap.get(namespace);

                int addCount = 0;
                int updateCount = 0;
                int deleteCount = 0;
                if ("default".equals(namespace)) {
                    namespace = "application";
                }
                for (Map.Entry<String, String> entry: curTemp.entrySet()) {
                    String key = entry.getKey().trim();
                    String value = entry.getValue().trim();
                    String preValue = preTemp.get(key);
                    if (preValue == null) {
                        if (addCount == 0) {
                            addContent.append(ADD.replace("{}", namespace)).append("\n");
                        }
                        addContent.append(key).append(" = ").append(value).append("\n");
                        addFlag = true;
                        addCount++;
                    } else if (!preValue.trim().equals(value)) {
                        if (updateCount == 0) {
                            updateContent.append(UPDATE.replace("{}", namespace)).append("\n");
                        }
                        updateCount++;
                        updateFlag = true;
                        updateContent.append(key).append(" = ").append(value).append("\n");
                        preTemp.remove(key);
                    } else {
                        preTemp.remove(key);
                    }
                }

                for (Map.Entry<String, String> entry: preTemp.entrySet()) {
                    if (deleteCount == 0) {
                        deleteContent.append(DELETE.replace("{}", namespace)).append("\n");
                    }
                    deleteCount++;
                    deleteFlag = true;
                    deleteContent.append(entry.getKey().trim()).append(" = ").append(entry.getValue().trim()).append("\n");
                }
            }

            if (addFlag || updateFlag || deleteFlag) {
                if (!addFlag) {
                    addContent = new StringBuilder();
                }

                if (!deleteFlag) {
                    deleteContent = new StringBuilder();
                }

                if (!updateFlag) {
                    updateContent = new StringBuilder();
                }

                for (String env: ENVS) {
                    String path = destPath + "/" + service + "/" + env + DEFAULT_DIR;
                    writeFile(new File(path), addContent + deleteContent.toString() + updateContent);
                }
            }
        }
    }

    public static int hasCount(String str, String cha) {
        int count = 0;
        while (str.contains("=")) {
            int index = str.indexOf(cha);
            str = str.substring(index +1);
            count++;
        }
        return count;
    }
}
